feat: granular text selection + workflow on-response example - #55
Merged
Conversation
Two items from the deferred audit list.
## Granular text selection
Selection model was line-only: clicking a chat line selected the
whole line, dragging extended row-by-row. Users couldn't select a
substring within a single line (a paragraph fragment, half a
filename, the right half of a diff hunk).
Selection anchors are now `(buffer_line_index, char_offset_in_line)`
pairs instead of `Option<usize>`. New `Renderer::buffer_pos_at(row,
col) -> Option<(usize, usize)>` maps mouse coordinates to a buffer
position, accounting for `content_indent()` and clamping at
end-of-line so dragging past the right edge anchors there.
The viewport paint splits each row into up to three painted runs
(pre-selection, selected with `Attribute::Reverse`, post-selection)
so highlighting happens per-character within a line. Bold-glow
for bright colors is preserved across all three runs.
`selected_text` extracts:
- single-row: char slice [start_col, end_col)
- multi-row: tail of start row + full middle rows + head of end row
Reverse drag (end before start in row-major order) is normalized
so the extracted text is always the visible selection.
Mouse handlers (`MouseDown`, `MouseDrag`, `MouseUp`) now pass
`col` through to `buffer_pos_at` instead of dropping it.
UTF-8 safe: char counts use `chars().count()` everywhere, so
`é` and `🦀` each count as 1 char, not their byte widths.
## workflow.janet on-response example
New `plugins/response_inspector.janet` demonstrates the
`on-response` hook's two distinct capabilities:
1. **Pattern detection + notifications** — calls `harness/notify`
when the agent's reply contains a code block.
2. **Steering string return value** — returns a system-prompt
suffix to be applied on the next turn, asking the agent to
add inline comments when its previous code block lacked them.
Documents the difference between `on-response` (assistant text)
and `on-tool-end` (tool output, where `harness/replace-result`
belongs).
## Test plan
- [x] 6 new tests in `ui::renderer::tests`:
- single-row substring selection
- reverse-drag normalization
- multi-row spans (tail + middle + head)
- empty selection returns None
- UTF-8 char-index correctness (`café 🦀`)
- `buffer_pos_at` clamps past-EOL
- [x] `cargo test --features plugin` -> 612 pass, 0 fail.
- [x] Both build profiles -> 0 warnings.
- [ ] Eyeball: drag in chat to select a substring; verify
reverse-video highlight only covers the selected chars,
clipboard receives the right substring.
allen-munsch
pushed a commit
to allen-munsch/dirge
that referenced
this pull request
Jun 3, 2026
…ode#55) Two items from the deferred audit list. ## Granular text selection Selection model was line-only: clicking a chat line selected the whole line, dragging extended row-by-row. Users couldn't select a substring within a single line (a paragraph fragment, half a filename, the right half of a diff hunk). Selection anchors are now `(buffer_line_index, char_offset_in_line)` pairs instead of `Option<usize>`. New `Renderer::buffer_pos_at(row, col) -> Option<(usize, usize)>` maps mouse coordinates to a buffer position, accounting for `content_indent()` and clamping at end-of-line so dragging past the right edge anchors there. The viewport paint splits each row into up to three painted runs (pre-selection, selected with `Attribute::Reverse`, post-selection) so highlighting happens per-character within a line. Bold-glow for bright colors is preserved across all three runs. `selected_text` extracts: - single-row: char slice [start_col, end_col) - multi-row: tail of start row + full middle rows + head of end row Reverse drag (end before start in row-major order) is normalized so the extracted text is always the visible selection. Mouse handlers (`MouseDown`, `MouseDrag`, `MouseUp`) now pass `col` through to `buffer_pos_at` instead of dropping it. UTF-8 safe: char counts use `chars().count()` everywhere, so `é` and `🦀` each count as 1 char, not their byte widths. ## workflow.janet on-response example New `plugins/response_inspector.janet` demonstrates the `on-response` hook's two distinct capabilities: 1. **Pattern detection + notifications** — calls `harness/notify` when the agent's reply contains a code block. 2. **Steering string return value** — returns a system-prompt suffix to be applied on the next turn, asking the agent to add inline comments when its previous code block lacked them. Documents the difference between `on-response` (assistant text) and `on-tool-end` (tool output, where `harness/replace-result` belongs). ## Test plan - [x] 6 new tests in `ui::renderer::tests`: - single-row substring selection - reverse-drag normalization - multi-row spans (tail + middle + head) - empty selection returns None - UTF-8 char-index correctness (`café 🦀`) - `buffer_pos_at` clamps past-EOL - [x] `cargo test --features plugin` -> 612 pass, 0 fail. - [x] Both build profiles -> 0 warnings. - [ ] Eyeball: drag in chat to select a substring; verify reverse-video highlight only covers the selected chars, clipboard receives the right substring. Co-authored-by: Yogthos <yogthos@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Switches chat selection from line-granular to character-granular: anchors are now
(line_idx, char_col)pairs, mouse coords map through newRenderer::buffer_pos_at(clamps at EOL), and viewport paint splits each row into up-to-three runs so highlighting is per-character. Plus a newplugins/response_inspector.janetexample demonstratingon-responsenotifications + return-string steering. 612 tests pass.